#pragma once
#include<iostream>
#include<unordered_map>
#include<assert.h>
#include<vector>
#include<string.h>
#include<functional>
#include<sys/epoll.h>
#include"logger.hpp"

class Poller;
class Channel
{    
public:
    using EventCallback=std::function<void()>;
    Channel(int fd,Poller* poller):_fd(fd),_event(0),_revents(0),_poller(poller){}
    int FD(){return _fd;}
    uint32_t Revent(){return _revents;}
    //获取想要监控的事件
    uint32_t Events(){return _event;}
    //设置实际就绪的事件
    void SetRevents(uint32_t events){_revents=events;}
    /*可读事件被触发的回调函数*/
    void SetReadCallback(const EventCallback& cb){_read_callback=cb;}
    /*可写事件被触发的回调函数*/
    void SetWriteCallback(const EventCallback& cb){_write_callback=cb;}
    /*错误事件被触发的回调函数*/
    void SetErrorCallback(const EventCallback& cb){_error_callback=cb;}
    /*连接断开事件被触发的回调函数*/
    void SetCloseCallback(const EventCallback& cb){_close_callback=cb;}
    /*任意事件被触发的回调函数*/
    void SetEventCallback(const EventCallback& cb){_event_callback=cb;}
    /*当前是否监控了可读*/
    bool ReadAble(){return (_event&EPOLLIN);}
    /*当前是否监控了可写*/
    bool WriteAble(){return (_event&EPOLLOUT);}
    /*启动读事件监控*/
    void EnableRead(){_event|=EPOLLIN;Update();}
    /*启动写事件监控*/
    void EnableWrite(){_event|=EPOLLOUT;Update();}
    /*关闭读事件监控*/
    void DisableRead(){_event&=~EPOLLIN;Update();}
    /*关闭写事件监控*/
    void DisableWrite(){_event&=~EPOLLOUT;Update();}
    /*关闭所有事件监控*/
    void DisableAll(){_event=0;Update(); }
    /*移除监控*/
    void Remove();
    void Update();
    /*事件处理，一旦连接触发了事件，就调用这个函数*/
    void HandleEvent(){
        if((_revents&EPOLLIN)||(_revents&EPOLLRDHUP)||(_revents&EPOLLPRI)){
            /*不管任何事件，都调用的回调函数*/
            if(_event_callback) _event_callback();
            if(_read_callback)  _read_callback();
        }
        /*有可能会释放连接的操作事件，一次只处理一个*/
        if(_revents&EPOLLOUT){//调试发现这里使用if,如果写端关闭了，调用了可读事件，把channel关闭了，就还会进来这里，会导致调用里面任何一个，但是chanel已经没了，就会出现段错误
            // /*不管任何事件，都调用的回调函数*/
            if(_event_callback) _event_callback();//事件处理之后再调用，刷新活跃度，防止被释放
            if(_write_callback)  _write_callback();//进行写入的时候，发现连接断开了，写入出错了，就要释放套接字
        }
        else if(_revents&EPOLLERR){
            if(_event_callback) _event_callback();
            if(_error_callback)  _error_callback();//一旦出错了，就释放连接了，连接都释放了，就没必要去调用任意事件的回调了，因此要放在前面
        }
        else if(_revents&EPOLLHUP){
            if(_event_callback) _event_callback();
            if(_close_callback)  _close_callback();
        }
        
    }
    ~Channel(){
        _read_callback=nullptr;
        _write_callback=nullptr;
        _error_callback=nullptr;
        _close_callback=nullptr;
        _event_callback=nullptr;
        //DEBUG：这个根据不同的系统，默认情况下，
        // 调用Channel的默认析构函数中，对于成员函数不会置空，这里需要手动写一个Channel的析构函数，
        // 在析构函数中，将各种回调函数都置为nullptr--->解决客户端关闭，这里会访问已经释放的channel而导致的系统崩溃

    }
private:
    int _fd;
    uint32_t _event;//当前需要监控的事件
    uint32_t _revents;//当前连接触发的事件--监控之后实际触发的事件
    Poller* _poller;
    EventCallback _read_callback;
    EventCallback _write_callback;
    EventCallback _error_callback;
    EventCallback _close_callback;
    EventCallback _event_callback;
};





#define MAX_EPOLLEVENTS 1024
class Poller
{   
private:
    //对epoll进行操作，调用epoll_ctl，
    void Update(Channel* channel,int op){
        //int epoll_ctl(int epfd,int op,int fd,struct epoll_event *ev)
        int fd=channel->FD();
        struct epoll_event ev;
        ev.data.fd=fd;
        ev.events=channel->Events();
        int ret=epoll_ctl(_epfd,op,fd,&ev);
        if(ret<0){
            ELOG(" epollctl failed");
            abort();
        }
    }
    //判断一个channel是否已经添加了事件监控
    bool HasChannel(Channel* channel){
        auto it = _channels.find(channel->FD());
        if(it==_channels.end())return false;
        return true;
    }

public:
    Poller(){
        _epfd=epoll_create(88);
        if(_epfd<0){
            ELOG("epoll create failed");
            abort();
        }
    }
    /*添加或修改监控事件*/
    void UpdateEvent(Channel* channel){
        bool ret=HasChannel(channel);
        if(ret==false)  {
            _channels.insert(std::make_pair(channel->FD(),channel));
            return Update(channel,EPOLL_CTL_ADD);
        }
        return Update(channel,EPOLL_CTL_MOD);
    }
    /*移除监控*/
    void RemoveEvent(Channel* channel){
        auto it = _channels.find(channel->FD());
        if(it!=_channels.end()) //由于这里写成了等号，导致释放了一个空的，只要涉及移除，就会出错----好像又没出错了，奇怪，待定
            _channels.erase(it);
        Update(channel,EPOLL_CTL_DEL);
    }
    /*开始监控，返回活跃连接*/
    void Poll(std::vector<Channel*>*active){
        //int epoll_wait(int epfd,struct epoll_event *evs,int maxevents,int timout)
        //nfds就绪的事件个数
        int nfds=epoll_wait(_epfd,_evs,MAX_EPOLLEVENTS,-1);//使用阻塞监控，没有描述符就绪，就一直在这监控
        if(nfds<0){
            if(errno==EINTR) return;//如果是被信号打断了就直接返回，只是给信号打断了阻塞而已，可以继续监控下一次的
            ELOG("EPOLL WAIT ERROR:%s\n",strerror(errno));//如果是其他错误，无论是什么错误都会导致大量描述符无法进行事件监控处理
            abort(); 
        }
        for(int i=0;i<nfds;i++){
            auto it=_channels.find(_evs[i].data.fd);
            assert(it!=_channels.end());//按理来说，我能够获取到，我能够对一个描述符进行事件监控，能够就绪事件，那他必然存在于所管理的_channels里的
            //如果他不存在，描述符就绪了，但是又找不到channels信息，就是异常了，代表管理出现问题了
            it->second->SetRevents(_evs[i].events);
            active->push_back(it->second);
        }
    }
private:
    int _epfd;
    struct epoll_event _evs[MAX_EPOLLEVENTS];
    std::unordered_map<int,Channel*> _channels;
};

void Channel::Remove(){return _poller->RemoveEvent(this);}
void Channel::Update(){return _poller->UpdateEvent(this);}